#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

//udp 是不需要进行连接、不可靠、面向数据报的传输
//封装 udp 接口

class UdpSocket{
	private:
		int _sockfd;   //套接字描述符
	public:
		UdpSocket():_sockfd(-1) {}   //构造函数
		~UdpSocket(){
			//析构函数
      Close();
		}

		//1、创建套接字
		bool Socket()
		{
			_sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);   //ipv4 地址域通信，数据报传输，udp 协议
			//成功返回套接字描述符，失败返回 -1
			if(_sockfd<0){
				perror("socket error!\n");
				return false;
			}
			return true;
		}

		//2、绑定地址信息
		bool Bind(const std::string& ip,uint16_t port)  //端口数据类型为uint16_t
		{
			//定义 ipv4 地址结构类型
			struct sockaddr_in addr;
			addr.sin_family=AF_INET;  //ipv4 地址域通信
			addr.sin_port=htons(port);  //转换为网络字节序
			addr.sin_addr.s_addr=inet_addr(ip.c_str());  //IP 地址转换为整型
			socklen_t len=sizeof(struct sockaddr_in);

			int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
			if(ret<0){
				perror("bind error");
				return false;
			}
			return true;
		}

		
		//接收数据
		//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

		bool Recv(std::string& body,std::string* ip=NULL,uint16_t* port=NULL)
		{
				struct sockaddr_in addr;
				socklen_t len=sizeof(struct sockaddr_in);
				char tem[4096]={0};
				int ret= recvfrom(_sockfd,tem,4095,0,(struct sockaddr*)&addr,&len);
				if(ret<0){
					perror("recvfrom error");
					return false;
				}					
				if(ip!=NULL)
					*ip=inet_ntoa(addr.sin_addr);
				if(port!=NULL)
					*port=ntohs(addr.sin_port);   //转换为主机字节序
				body.assign(tem,ret);
				return true;
		}

    //发送数据
    //ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    bool Send(const std::string& body,const std::string& ip,uint16_t port)
    {
      struct sockaddr_in addr;
      addr.sin_family=AF_INET;
      addr.sin_port=htons(port);  //转换为网络字节序
      addr.sin_addr.s_addr=inet_addr(ip.c_str());  //转换为整型IP
      socklen_t len=sizeof(struct sockaddr_in);
      int ret=sendto(_sockfd,body.c_str(),body.size(),0,(struct sockaddr*)&addr,len);
      if(ret<0){
        perror("sendto error!\n");
        return false;
      }
      return true;
    }

    //关闭套接字
    bool Close()
    {
      if(_sockfd!=-1){
        close(_sockfd);
        _sockfd=-1;
      }
      return true;
    }
};
